package com.hengfoxwolf.definerecyclerviewlayoutmanagerdemo.define
import android.content.Context
import android.graphics.Rect
import android.util.Log
import android.util.SparseArray
import android.util.SparseBooleanArray
import android.view.View.MeasureSpec
import androidx.core.util.set
import androidx.recyclerview.widget.RecyclerView

class VerticalLayoutManager : RecyclerView.LayoutManager() {

    /*
    * 问题点:
    *   1.垂直布局情况下 RecyclerView高度为WrapContent时显示不了item --- 已解决
    *   1.垂直布局情况下 RecyclerView高度为WrapContent时 item总高度值没有超过屏幕高度  向上或下滑动时  结果不正确 --- 已解决
    * */


    /*
    * 记录在layoutChild时，每个item的初始位置
    * */
    private val itemFrames by lazy {
        SparseArray<Rect?>()
    }

    /*
     * 记录item是否已经addView并且layout了
     * （这里很关键，如果滑动到最顶部时，每次滑动，虽然距离是0，但是会一直将View加入RecyclerView中，导致oom，这里主要主要是用来规避这一点）
     * 详情见recycleAndFillChild方法
      */
    private val layoutFlag by lazy {
        SparseBooleanArray()
    }

    /*
    * 在LayoutChild时，计算出所有item的高度和
    * */
    private var totalHeight = 0

    /*
    * 滑动时，滚动的距离
    * */
    private var offset = 0

    override fun isAutoMeasureEnabled(): Boolean = true

    override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) {
        super.onLayoutChildren(recycler, state)

        detachAndScrapAttachedViews(recycler!!)

        var offsetY = 0
        totalHeight = 0
        Log.d(TAG, "itemCount:${itemCount}")
        for (i in 0.until(itemCount)) {
            val view = recycler.getViewForPosition(i)
            addView(view)
            measureChildWithMargins(view, 0, 0)

            val viewWidth = getDecoratedMeasuredWidth(view)
            val viewHeight = getDecoratedMeasuredHeight(view)
            Log.d(TAG, "viewWidth:${viewWidth},viewHeight:${viewHeight}")
            layoutDecorated(
                view,
                0,
                offsetY,
                viewWidth,
                offsetY + viewHeight
            )

            val rect = itemFrames[i]
            if (rect == null) {
                itemFrames[i] = Rect(0, offsetY, viewWidth, offsetY + viewHeight)
            } else {
                itemFrames[i]?.set(0, offsetY, viewWidth, offsetY + viewHeight)
            }

            val hadLayout = layoutFlag[i]
            layoutFlag[i] = !hadLayout

            offsetY += viewHeight
            totalHeight += viewHeight
        }

        Log.d(TAG, "totalHeight:${totalHeight}")

        /*
        * 如果调用了requestLayout，会导致RecyclerView重绘，从而重新布局子View，那么布局完子View后，将其回滚至原来的位置。
        * */
        offsetChildrenVertical(-offset)
    }

    override fun scrollVerticallyBy(
        dy: Int,
        recycler: RecyclerView.Recycler?,
        state: RecyclerView.State?
    ): Int {
        var travel = dy
        if (offset + dy < 0) {
            /*
            * 滑动到顶部时，限制不能滑出去
            * */
            Log.d(TAG, "----滑动到顶部----travel:${travel}")
            travel = -offset
        } else if (offset + dy > totalHeight - getVerticalSpace()) {
            /*
            * 滑动到底部时，限制不能滑出去
            * */
            Log.d(TAG, "****滑动到底部****travel0:${travel}")
            travel = totalHeight - getVerticalSpace() - offset
            Log.d(TAG, "****滑动到底部****travel1:${travel}")
        }

        offset += travel

        offsetChildrenVertical(-travel)
        recycleAndFillChild(recycler, state)

        return travel
    }


    override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams =
        RecyclerView.LayoutParams(
            RecyclerView.LayoutParams.WRAP_CONTENT,
            RecyclerView.LayoutParams.WRAP_CONTENT
        )

    override fun canScrollVertically(): Boolean = true

    private fun recycleAndFillChild(
        recycler: RecyclerView.Recycler?,
        state: RecyclerView.State?
    ) {
        if (state?.isPreLayout!!) {
            return
        }

        val displayArea =
            Rect(0, offset, getHorizontalSpace(), offset + getVerticalSpace())
        /*
        * 将已经加入到RecyclerView并且显示的View中过滤出将要移出界面的View
        * */
        var childFrame = Rect()
        for (i in 0.until(childCount)) {
            /*
            * getChildAt表示获取通过addView加入到RecyclerView中的View
            * */
            val child = getChildAt(i)
            child?.apply {
                childFrame.set(
                    getDecoratedLeft(this),
                    getDecoratedTop(this),
                    getDecoratedRight(this),
                    getDecoratedBottom(this),
                )

                if (!Rect.intersects(displayArea, childFrame)) {
                    val removePosition = getPosition(this)
                    layoutFlag[removePosition] = false
                    /*
                    * 不需要显示的View，先放到Recycle里面待循环利用
                    * */
                    if (recycler != null) {
                        removeAndRecycleView(this, recycler)
                    }
                }
            }
        }

        /*
        * 主要到这里是getItemCount，跟上面getChildCount不一样
        * */
        for (i in 0.until(itemCount)) {
            if (Rect.intersects(displayArea, itemFrames[i]!!)) {
                /*
                * 这里是避免重复addView同一个View
                * */
                val notAdded = !layoutFlag[i]
                if (notAdded) {
                    val child = recycler?.getViewForPosition(i)
                    child?.let {
                        measureChildWithMargins(it, 0, 0)
                        addView(it)
                        childFrame = itemFrames[i]!!
                        layoutDecorated(
                            it,
                            childFrame.left,
                            childFrame.top - offset,
                            childFrame.right,
                            childFrame.bottom - offset
                        )
                        layoutFlag[i] = true
                    }
                }
            }
        }
    }

    private fun getVerticalSpace() = height - paddingTop - paddingBottom

    private fun getHorizontalSpace() = width - paddingStart - paddingEnd

    companion object {
        const val TAG = "VerticalLayoutManager"
    }

}